package cn.iocoder.yudao.module.project.service.productioncode;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.project.dal.dataobject.internalcode.InternalCodeDO;
import cn.iocoder.yudao.module.project.dal.dataobject.kismaterialreceipt.KisMaterialReceiptDO;
import cn.iocoder.yudao.module.project.dal.dataobject.kismaterielexwarehouse.KisMaterielExWarehouseDO;
import cn.iocoder.yudao.module.project.dal.dataobject.supplier.SupplierDO;
import cn.iocoder.yudao.module.project.dal.dataobject.workstation.WorkstationDO;
import cn.iocoder.yudao.module.project.dal.mysql.internalcode.InternalCodeMapper;
import cn.iocoder.yudao.module.project.dal.mysql.kismaterialreceipt.KisMaterialReceiptMapper;
import cn.iocoder.yudao.module.project.dal.mysql.kismaterielexwarehouse.KisMaterielExWarehouseMapper;
import cn.iocoder.yudao.module.project.dal.mysql.supplier.SupplierMapper;
import cn.iocoder.yudao.module.project.dal.mysql.workstation.WorkstationMapper;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

import cn.iocoder.yudao.module.project.controller.admin.productioncode.vo.*;
import cn.iocoder.yudao.module.project.dal.dataobject.productioncode.ProductionCodeDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.project.dal.mysql.productioncode.ProductionCodeMapper;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.project.config.projectDefine.HTTP_STATUS.ERROR_STATUS;
import static cn.iocoder.yudao.module.project.config.projectDefine.WEIGHT_STATUS.*;
import static cn.iocoder.yudao.module.project.enums.ErrorCodeConstants.*;

/**
 * 成品码信息 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
public class ProductionCodeServiceImpl implements ProductionCodeService {

    @Resource
    private ProductionCodeMapper productionCodeMapper;

    @Resource
    private InternalCodeMapper internalCodeMapper;

    @Resource
    private KisMaterialReceiptMapper kisMaterialReceiptMapper;

    @Resource
    private SupplierMapper supplierMapper;

    @Resource
    private KisMaterielExWarehouseMapper kisMaterielExWarehouseMapper;

    @Resource
    private PermissionApi permissionApi;

    @Resource
    private AdminUserApi adminUserApi;

    @Resource
    private WorkstationMapper workstationMapper;

    @Resource
    private DictDataApi dictDataApi;

    @Override
    public String createProductionCode(ProductionCodeSaveReqVO createReqVO) {
        // 插入
        ProductionCodeDO productionCode = BeanUtils.toBean(createReqVO, ProductionCodeDO.class);
        productionCodeMapper.insert(productionCode);
        // 返回
        return productionCode.getId();
    }

    @Override
    public void updateProductionCode(ProductionCodeSaveReqVO updateReqVO) {
        // 校验存在
        validateProductionCodeExists(updateReqVO.getId());
        // 更新
        ProductionCodeDO updateObj = BeanUtils.toBean(updateReqVO, ProductionCodeDO.class);
        productionCodeMapper.updateById(updateObj);
    }

    @Override
    public void deleteProductionCode(String id) {
        // 校验存在
        validateProductionCodeExists(id);
        // 删除
        productionCodeMapper.deleteById(id);
    }

    private void validateProductionCodeExists(String id) {
        if (productionCodeMapper.selectById(id) == null) {
            throw exception(PRODUCTION_CODE_NOT_EXISTS);
        }
    }

    @Override
    public ProductionCodeDO getProductionCode(String id) {
        return productionCodeMapper.selectById(id);
    }

    @Override
    public PageResult<ProductionCodeDO> getProductionCodePage(ProductionCodePageReqVO pageReqVO) {
        return productionCodeMapper.selectPage(pageReqVO);
    }




    @Override
    public String boxScan(ProductionCodeSaveReqVO createReqVO) {
        String internalCode = createReqVO.getInternalCode();
        String id = createReqVO.getId();

        try {
            if (StringUtils.isBlank(internalCode)) {
                Assert.notNull(null,"未关联内码,请重新关联内码");
            }
            // 根据内码查询订单编号/型号
            InternalCodeDO internalCodeDO = internalCodeMapper.selectById(internalCode);
            if (null == internalCodeDO) {
                Assert.notNull(null, "内码" + internalCode + "未关联生产订单和型号");
            }

            String orderCode = internalCodeDO.getOrderCode();
            String type = internalCodeDO.getType();
            createReqVO.setOrderCode(orderCode);
            createReqVO.setType(type);

            ProductionCodeDO productionCode = BeanUtils.toBean(createReqVO, ProductionCodeDO.class);


            productionCode.setAssociatedWorkstationId(createReqVO.getAssociatedWorkstationId());
            productionCode.setAssociatedWorkstationUserId(createReqVO.getAssociatedWorkstationUserId());

            // 如果成品码未绑定内码则绑定
            if (productionCodeMapper.selectById(id) == null) {
                productionCodeMapper.insert(productionCode);
            }
            if (productionCodeMapper.selectById(id) != null) {
                productionCodeMapper.updateById(productionCode);
            }
        }
        catch (MybatisPlusException mpe) {
            throw new ServiceException(ERROR_STATUS, mpe.getMessage());
        }

        return id;
    }

    @Override
    public List<ProductRetrospectVO> selectProductRetrospect(Map<String, Object> params) {
        List<ProductRetrospectVO> productRetrospectVOS = productionCodeMapper.selectProductRetrospect(params);
        if (CollectionUtil.isNotEmpty(productRetrospectVOS)) {
            // 设置供应商,入库时间,出入库操作员
            for (ProductRetrospectVO productRetrospectVO : productRetrospectVOS) {
                String batchCode = productRetrospectVO.getBatchCode();
                String materialCode = productRetrospectVO.getMaterialCode();
                QueryWrapper<KisMaterialReceiptDO> kisMaterialReceiptDOQueryWrapper = new QueryWrapper<>();
                kisMaterialReceiptDOQueryWrapper.eq("code", batchCode);
                kisMaterialReceiptDOQueryWrapper.eq("material_code", materialCode);
                List<KisMaterialReceiptDO> kisMaterialReceiptDOS = kisMaterialReceiptMapper.selectList(kisMaterialReceiptDOQueryWrapper);
                if (CollectionUtil.isNotEmpty(kisMaterialReceiptDOS)) {
                    KisMaterialReceiptDO kisMaterialReceiptDO = kisMaterialReceiptDOS.get(0);
                    String supplierId = kisMaterialReceiptDO.getSupplier();
                    LocalDateTime receiptDate = kisMaterialReceiptDO.getReceiptDate();
                    String operator = kisMaterialReceiptDO.getOperator();
                    String format = LocalDateTimeUtil.format(receiptDate, "yyyy-MM-dd HH:mm:ss");
                    DateTime parse = DateUtil.parse(format, "yyyy-MM-dd");
                    productRetrospectVO.setSupplierId(supplierId);
                    productRetrospectVO.setReceiptTime(parse);
                    productRetrospectVO.setOperator(operator);
                }
            }

            // 格式化供应商
            List<SupplierDO> supplierDOS = supplierMapper.selectList();
            for (ProductRetrospectVO productRetrospectVO : productRetrospectVOS) {
                for (SupplierDO supplierDO : supplierDOS) {
                    String name = supplierDO.getName();
                    String supplierId = supplierDO.getId();
                    if (StringUtils.equals(productRetrospectVO.getSupplierId(), supplierId)) {
                        productRetrospectVO.setSupplierName(name);
                    }
                }
            }

            // 设置出库时间
            for (ProductRetrospectVO productRetrospectVO : productRetrospectVOS) {
                String exCode = productRetrospectVO.getExCode();
                String orderCode = productRetrospectVO.getOrderCode();
                String type = productRetrospectVO.getType();
                String materialCode = productRetrospectVO.getMaterialCode();
                QueryWrapper<KisMaterielExWarehouseDO> kisMaterielExWarehouseDOQueryWrapper = new QueryWrapper<>();
                kisMaterielExWarehouseDOQueryWrapper.eq("ex_code", exCode);
                kisMaterielExWarehouseDOQueryWrapper.eq("order_code", orderCode);
                kisMaterielExWarehouseDOQueryWrapper.eq("type", type);
                kisMaterielExWarehouseDOQueryWrapper.eq("code", materialCode);
                List<KisMaterielExWarehouseDO> kisMaterielExWarehouseDOS = kisMaterielExWarehouseMapper.selectList(kisMaterielExWarehouseDOQueryWrapper);
                if (CollectionUtil.isNotEmpty(kisMaterielExWarehouseDOS)) {
                    KisMaterielExWarehouseDO kisMaterielExWarehouseDO = kisMaterielExWarehouseDOS.get(0);
                    LocalDateTime deliveryDate = kisMaterielExWarehouseDO.getUpdateTime();
                    String format = LocalDateTimeUtil.format(deliveryDate, "yyyy-MM-dd HH:mm:ss");
                    DateTime parse = DateUtil.parse(format, "yyyy-MM-dd HH:mm:ss");
                    productRetrospectVO.setDeliveryTime(parse);
                }
            }
        }
        return productRetrospectVOS;
    }

    @Override
    public Long countProductRetrospect(Map<String, Object> params) {
        Long count = productionCodeMapper.countProductRetrospect(params);
        return count;
    }

    @Override
    public List<DefectiveProductVO> selectDefectiveProduct(Map<String, Object> params) {
        List<DefectiveProductVO> defectiveProductVOS = productionCodeMapper.selectDefectiveProduct(params);
        return defectiveProductVOS;
    }

    @Override
    public Long countDefectiveProduct(Map<String, Object> params) {
        Long count = productionCodeMapper.countDefectiveProduct(params);
        return count;
    }

    @Override
    public List<MaterialBatchVO> selectMaterialBatch(Map<String, Object> params) {
        List<MaterialBatchVO> materialBatchVOS = productionCodeMapper.selectMaterialBatch(params);
        return materialBatchVOS;
    }

    @Override
    public Long countMaterialBatch(Map<String, Object> params) {
        Long count = productionCodeMapper.countMaterialBatch(params);
        return count;
    }

    @Override
    public List<DefectiveProductVO> selectMaterialRigidity(Map<String, Object> params) {
        List<DefectiveProductVO> defectiveProductVOS = productionCodeMapper.selectMaterialRigidity(params);
        return defectiveProductVOS;
    }

    @Override
    public Long countMaterialRigidity(Map<String, Object> params) {
        Long count = productionCodeMapper.countMaterialRigidity(params);
        return count;
    }

    @Override
    public List<MaterialReceiptVO> selectMaterialReceipt(Map<String, Object> params) {
        List<MaterialReceiptVO> materialReceiptVOS = productionCodeMapper.selectMaterialReceipt(params);
        return materialReceiptVOS;
    }

    @Override
    public Long countMaterialReceipt(Map<String, Object> params) {
        Long count = productionCodeMapper.countMaterialReceipt(params);
        return count;
    }

    @Override
    public List<MaterialDeliveryVO> selectMaterialDelivery(Map<String, Object> params) {
        List<MaterialDeliveryVO> materialDeliveryVOS = productionCodeMapper.selectMaterialDelivery(params);
        return materialDeliveryVOS;
    }

    @Override
    public Long countMaterialDelivery(Map<String, Object> params) {
        Long count = productionCodeMapper.countMaterialDelivery(params);
        return count;
    }

    @Override
    public List<RealtimeProductVO> selectRealtimeProduct() {
        Long loginUserId = WebFrameworkUtils.getLoginUserId();

        boolean commonFlag = permissionApi.hasAnyRoles(loginUserId, "longyi_common");
        boolean adminFlag = permissionApi.hasAnyRoles(loginUserId, "longyi_admin");
        boolean managerFlag = permissionApi.hasAnyRoles(loginUserId, "longyi_manager");


        // List<RealtimeProductVO> realtimeProductVOS = productionCodeMapper.selectRealtimeProduct();
        List<RealtimeProductVO> realtimeProductVOS = new ArrayList<>();
        QueryWrapper<InternalCodeDO> internalCodeQueryWrapper = new QueryWrapper<>();
        // internalCodeQueryWrapper.isNotNull("weight");
        List<InternalCodeDO> internalCodeDOS = null;

        if(adminFlag){
            internalCodeQueryWrapper.apply("!(weight is null and (rigidity is null or rigidity = '') and (balance is null or balance = ''))");
            internalCodeQueryWrapper.orderByDesc("operating_time").last("limit 10");
            internalCodeDOS = internalCodeMapper.selectList(internalCodeQueryWrapper);
        }else if(managerFlag){

            AdminUserRespDTO adminUserRespDTO = adminUserApi.getUser(loginUserId);
            Long deptId = adminUserRespDTO.getDeptId();
            List<WorkstationDO> workstationDOList = workstationMapper.selectList(WorkstationDO::getDepartment, deptId);
            List<Integer> workstationIdList = workstationDOList.stream().map(workstationDO -> workstationDO.getId()).collect(Collectors.toList());

            internalCodeQueryWrapper.apply("!(weight is null and (rigidity is null or rigidity = '') and (balance is null or balance = ''))");
            internalCodeQueryWrapper.and(qw -> qw.in("test_workstation_id", workstationIdList).or().in("balance_twice_workstation_id", workstationIdList));
            internalCodeQueryWrapper.orderByDesc("operating_time").last("limit 10");
            internalCodeDOS = internalCodeMapper.selectList(internalCodeQueryWrapper);
        }else if(commonFlag){
            internalCodeQueryWrapper.apply("!(weight is null and (rigidity is null or rigidity = '') and (balance is null or balance = ''))");
            internalCodeQueryWrapper.and(qw -> qw.eq("test_workstation_user_id", loginUserId).or().eq("balance_twice_workstation_user_id", loginUserId));
            internalCodeQueryWrapper.orderByDesc("operating_time").last("limit 10");
            internalCodeDOS = internalCodeMapper.selectList(internalCodeQueryWrapper);
        }


        if (CollectionUtil.isNotEmpty(internalCodeDOS)) {
            for (InternalCodeDO internalCodeDO : internalCodeDOS) {
                RealtimeProductVO realtimeProductVO = new RealtimeProductVO();
                String internalCode = internalCodeDO.getId();
                String orderCode = internalCodeDO.getOrderCode();
                Double weight = internalCodeDO.getWeight();
                Integer weightStatus = internalCodeDO.getWeightStatus();
                String rigidity = internalCodeDO.getRigidity();
                String balance = internalCodeDO.getBalance();
                LocalDateTime operatingTime = internalCodeDO.getOperatingTime();
                String format = DateUtil.format(operatingTime, "yyyy-MM-dd HH:mm:ss");
                DateTime operatingDate = DateUtil.parse(format);
                // Date operatingDate = Date.from(operatingTime.atZone(ZoneId.systemDefault()).toInstant());
                realtimeProductVO.setInternalCode(internalCode);
                realtimeProductVO.setOrderCode(orderCode);
                realtimeProductVO.setWeight(weight);
                realtimeProductVO.setWeightStatus(weightStatus);
                realtimeProductVO.setRigidity(rigidity);
                realtimeProductVO.setBalance(balance);
                realtimeProductVO.setProductTime(operatingDate);
                if (null != weightStatus && weightStatus.equals(WEIGHT_STATUS_0)) {
                    realtimeProductVO.setWeightStatusStr(WEIGHT_STATUS_0S);
                }
                if (null != weightStatus && weightStatus.equals(WEIGHT_STATUS_1)) {
                    realtimeProductVO.setWeightStatusStr(WEIGHT_STATUS_1S);
                }
                if (null != weightStatus && weightStatus.equals(WEIGHT_STATUS_2)) {
                    realtimeProductVO.setWeightStatusStr(WEIGHT_STATUS_2S);
                }
                realtimeProductVOS.add(realtimeProductVO);
            }

            List<String> internalCodes = internalCodeDOS.stream().map(c -> c.getId()).collect(Collectors.toList());
            QueryWrapper<ProductionCodeDO> productionCodeQueryWrapper = new QueryWrapper<>();
            productionCodeQueryWrapper.in("internal_code", internalCodes);
            List<ProductionCodeDO> productionCodeDOS = productionCodeMapper.selectList(productionCodeQueryWrapper);
            if (CollectionUtil.isNotEmpty(productionCodeDOS)) {
                for (RealtimeProductVO realtimeProductVO : realtimeProductVOS) {
                    for (ProductionCodeDO productionCodeDO : productionCodeDOS) {
                        if (StringUtils.equals(realtimeProductVO.getInternalCode(), productionCodeDO.getInternalCode())) {
                            String productCode = productionCodeDO.getId();
                            String productName = productionCodeDO.getName();
                            String type = productionCodeDO.getType();
                            LocalDateTime downlineTime = productionCodeDO.getDownlineTime();
                            Date downlineDate = Date.from(downlineTime.atZone(ZoneId.systemDefault()).toInstant());
                            realtimeProductVO.setProductCode(productCode);
                            realtimeProductVO.setProductName(productName);
                            realtimeProductVO.setType(type);
                            realtimeProductVO.setProductTime(downlineDate);
                        }
                    }
                }
            }
        }
        return realtimeProductVOS;
    }

    @Override
    public StatisticsVO selectStatisticsNum(String queryDate) {
        StatisticsVO statisticsVO = productionCodeMapper.selectStatisticsNum(queryDate);
        return statisticsVO;
    }

    @Override
    public Integer selectProDuctNumByOrderCode(Map<String, Object> params) {
        Integer proDuctNum = productionCodeMapper.selectProDuctNumByOrderCode(params);
        return proDuctNum;
    }

    @Override
    public void updateProductionCodeKey(UpdatedProductionCodeVO updatedProductionCodeVO) {

        validateProductionCodeExists(updatedProductionCodeVO.getSourceProductionCode());

        ProductionCodeDO existsDO = productionCodeMapper.selectById(updatedProductionCodeVO.getTargetProductionCode());
        if(!ObjectUtils.isEmpty(existsDO)){
            throw new ServiceException(ERROR_STATUS, "成品码已存在");
        }

        productionCodeMapper.updateProductionCodeKey(updatedProductionCodeVO);
    }
}
